/*    
    OpenMheg - A free MHEG-5 library
    Copyright (C) 2008 Dharmesh Malam

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.IO;
using Core.Model.Data.Type;
using Core.Parser.XMLParser;
using System.Xml;
using Core.Model.Events;
using System.Threading;
using Core.Model.Actions;
using Core.Model.Links;
using Core.Model.Data.Generics;
using Core.Model.Actions.Elementary;
using Core.Engine;
using Core.Model.Data;
using Core.Parser;

namespace Core.Model
{
    //threadsafe singleton
    public sealed class MHEGEngine
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );

        private static readonly MHEGEngine instance = new MHEGEngine();
        private object lockObj = new Object();

        public delegate void DisplayRedrawDelegate ( params System.Drawing.Rectangle [] rs );
        public event DisplayRedrawDelegate DisplayRedraw;

        //tell c# compiler not to mark type as beforefieldinit
        static MHEGEngine () { }
        MHEGEngine () { }

        public static MHEGEngine Instance
        {
            get
            {
                return instance;
            }
        }


        #region  Application, scene and app Stack
        private Application activeApplication = null;

        public Application ActiveApplication
        {
            get { return activeApplication; }
            set
            {
                activeApplication = value;
                log.Info( "Active Application = " + value );
            }
        }
        private Scene activeScene = null;

        public Scene ActiveScene
        {
            get { return activeScene; }
            set
            { 
                activeScene = value;
                log.Info( "Active Scene = " + value );
            }
        }

        public Group ActiveGroup
        {
            get
            {
                return ( Group ) ActiveScene ?? ( Group ) ActiveApplication;
            }
        }

        private DirectoryInfo applicationRoot;

        public DirectoryInfo ApplicationRoot
        {
            get { return applicationRoot; }
            set { applicationRoot = value; }
        }


        private Stream applicationStream;

        public Stream ApplicationStream
        {
            get { return applicationStream; }
            set { applicationStream = value; }
        }

        private Interactible activeInteractible = null;

        public Interactible ActiveInteractible
        {
            get { return activeInteractible; }
            set { activeInteractible = value; }
        }
        private Stack<ObjectRef> applicationStack = new Stack<ObjectRef>();

        public Stack<ObjectRef> ApplicationStack
        {
            get { return applicationStack; }
            //set { applicationStack = value; }
        }

        private MHEGDisplayStack displayStack = new MHEGDisplayStack();

        public MHEGDisplayStack DisplayStack
        {
            get { return displayStack; }
            //set { displayStack = value; }
        }

        private MHEGTuner tuner = new MHEGTuner();

        public MHEGTuner Tuner
        {
            get { return tuner; }
            //set { tuner = value; }
        }


        #endregion

        public void Initialise ()
        {
            //ItemsCache.Clear();
            applicationStack.Clear();
            ClearActiveApp();
            activeApplication = null;
            activeScene = null;
            Tuner.Initialise();
        }

        public void ClearActiveApp ()
        {
            ItemsCache.Clear();
            ClearTimers();
            ClearEventQs();
            DisplayStack.Clear();
            activeLinks.Clear();
            ApplicationStream = null;
            activeInteractible = null;
        }

        public void SetUp ( DisplayRedrawDelegate redrawDelegate )
        {
            if ( redrawDelegate != null )
            {
                DisplayRedraw = redrawDelegate;
            }
        }



        public void Run ( Application app )
        {
            log.Debug( "Starting application " + app.ToString() );
            Initialise();
            
            app.Launch();

            lock ( lockObj )
            {
                while ( !quit )
                {
                    ProcessEvents();

                    UpdateScreen();

                    Monitor.Wait( lockObj );
                    //Thread.Sleep( 20 );
                }
            }

            Initialise();
            //UpdateScreen();
            quit=false;
        }

        private bool quit = false;
        public void Quit ()
        {
            lock ( lockObj )
            {
                quit = true;
                Monitor.PulseAll( lockObj );
            }


        }
        private void UpdateScreen ()
        {
            System.Drawing.Rectangle [] rs = DisplayStack.Flush();

            if ( rs != null )
            {
                log.Debug( "Updating screen with "+rs.Length.ToString() + " updates" );
                OnDisplayRedraw( rs );
            }
        }

        private void OnDisplayRedraw ( System.Drawing.Rectangle [] rs )
        {
            IAsyncResult result = DisplayRedraw.BeginInvoke( rs, null, null );
            DisplayRedraw.EndInvoke( result );

        }



        #region  Object Lookup
        private Dictionary<ObjectRef, Root> itemsCache = new Dictionary<ObjectRef, Root>( 64 );

        public Dictionary<ObjectRef, Root> ItemsCache
        {
            get { return itemsCache; }
            set { itemsCache= value; }
        }


        private Root SearchActiveGroups ( ObjectRef reference )
        {
            Root ret = null;
            Group group = null;

            if ( ActiveApplication != null && 
                ActiveApplication.ObjectIdentifier.GroupIdentifier == reference.GroupIdentifier )
            {
                group = ActiveApplication;
            }
            else if ( ActiveScene != null &&
                ActiveScene.ObjectIdentifier.GroupIdentifier == reference.GroupIdentifier )
            {
                group = ActiveScene;
            }


            if ( group != null )
            {
                if ( reference.ObjectNumber == 0 )
                    ret = group;
                else

                    ret = group.Items.Find( delegate( Ingredient item )
                                    {
                                        return item.ObjectIdentifier.ObjectNumber == reference.ObjectNumber;
                                    } );

            }

            if ( ret ==null && ApplicationStream != null )
            {
                ret = ApplicationStream.Multiplex.Find( delegate( Ingredient item )
                               {
                                   return item.ObjectIdentifier.ObjectNumber == reference.ObjectNumber;
                               } );
            }


            if ( ret != null )
            {
                ItemsCache.Add( reference, ret );
                //log.Debug(ret+"("+reference+") retrieved for first time. Caching...");
            }

            return ret;

        }


        public Root Find ( ObjectRef reference )
        {
            if ( reference == null )
            {
                log.Error( "Find: Asked to dereference a null reference, returning null" );
                return null;
            }

            Root obj = null;

            if ( ItemsCache.ContainsKey( reference ) )
                obj = ItemsCache [reference];
            else
                obj = SearchActiveGroups( reference );

            if ( obj == null )
            {
                log.Info( "Engine trying to find external reference" + reference+"." );


                String path = reference.GroupIdentifier;

                if ( reference.ObjectNumber != 0 )
                {
                    //throw new NullReferenceException();
                    log.Error( "Engine asked to dereference a non Group object ("+reference.ToString()+
                             "). Current active group is "+ActiveGroup.ToString()+"." );
                    return null;
                }

                path = GetCanonicalPath( path );
                FileInfo resource = null;

                if ( path != null )
                {
                    resource = GetFile( path, null );
                }

                if ( resource != null )
                {
                    //if ( reference.GroupIdentifier.StartsWith( "~/" ) )
                    //    path =  ActiveApplication.ContentFile.DirectoryName +"\\"+ path.Substring( 2 );

                    //else if ( reference.GroupIdentifier.StartsWith( "/" ) )
                    //    path = ActiveApplication.ContentFile.DirectoryName +"\\"+ path.Substring( 1 );
                    //else throw new Exception( "Unknown filename" );


                    obj = MHEGParser.ParseMHEG( resource );

                    if ( obj == null )
                    {
                        log.Error( "Engine could not locate external object "+reference.ToString()
                    +"! Current active group is "+ActiveGroup.ToString()+"." );

                        MHEGEngine.Instance.AsyncHandle( EventFactory.Create( ActiveGroup, enumEvents.EngineEvent, new GenericInteger( 2 ) ) );
                    }

                }
            }
             //else
             //   log.Debug( "Engine found object" + reference.ToString() +"." );

            if (obj != null && !obj.ObjectIdentifier.Equals(reference))
            {

               // throw new Exception("Found wrong object");

            }

            return obj;

        }


        public void SetContent ( GenericContentRef reference, Content content, int hook )
        {

            if ( reference == null )
            {
                throw new NullReferenceException();
                log.Error( "Engine tring to set content with a null reference" );
                return;
            }

            if ( ( reference.Value.Value == "" || reference.Value.Value == " " ) )
            {
               // 
                if (hook == MHEGEngine.Instance.ActiveApplication.TextContentHook)
                    content.TextContent = "";
                //else
                  //  throw new NullReferenceException();

                log.Error( "Engine tring to set content with a empty reference" );
                return;

                //if ( Util.ContentHooks.IsBitmapContentHool( ContentHook.Value ) )
                //    return  null;
                //else
                //    return Content ="";

            }

            String path = GetCanonicalPath( reference.Value.Value );
            FileInfo resource = null;

            if ( path != null )
            {
                resource = GetFile( path, content );
            }

            if ( resource != null )
            {
                //if ( Util.DefaultContentHooks.IsBitmapContentHool( hook ) )
                if(hook == 4)
                {
                    try
                    {
                        log.Info( "Engine setContent: Loading bitmap resource from " + resource+")." );

                        System.Drawing.Image image = new System.Drawing.Bitmap( resource.FullName );
                        content.BitmapContent = image;
                        content.Reference = reference;
                        content.ContentRoot = resource.Directory;
                    }
                    catch ( IOException e )
                    {
                        content.BitmapContent = null;
                        log.Warn( "Engine setContent: Bitmap "+reference+" could not be read.", e );

                    }
                }
                else if ( hook == 2 ) // M2V file
                {                    
                    try
                    {
                        log.Info( "Engine setContent: Loading bitmap resource from " + resource+")." );

                        //System.Drawing.Image image = new System.Drawing.Bitmap( resource.FullName );
                        //content.BitmapContent = image;
                        content.Reference = reference;
                        content.ContentRoot = resource.Directory;
                    }
                    catch ( IOException e )
                    {
                        content.BitmapContent = null;
                        log.Warn( "Engine setContent: Bitmap "+reference+" could not be read.", e );

                    }
                }
                else if ( hook == ( MHEGEngine.Instance.ActiveApplication.TextContentHook ) )
                {
                    try
                    {
                        log.Info( "Engine setContent: Loading text resource from " + resource+")." );

                        using ( StreamReader textReader =  new StreamReader( resource.FullName, Encoding.Default ) )
                        {
                            content.TextContent = textReader.ReadToEnd();
                            textReader.Close();
                            content.ContentRoot = resource.Directory;
                            content.Reference = reference;
                            char [] cs = content.TextContent.ToCharArray();

                        }
                    }

                    catch ( IOException e )
                    {
                        content.TextContent = "";
                        log.Warn( "Engine setContent: Text "+reference+" could not be read.", e );

                    }
                }
            }
            else
            {
                MHEGEngine.Instance.AsyncHandle( EventFactory.Create( MHEGEngine.Instance.ActiveGroup, enumEvents.EngineEvent, new GenericInteger( 3 ) ) );
                log.Error( "Engine SetContent: Content does not exist file. ("+reference+")" );
                //throw new NullReferenceException();

                if ( hook == ActiveApplication.TextContentHook )
                    content.TextContent = "";
                
                return;

            }

        }

        private FileInfo GetFile ( String path, Content oldContent )
        {
            FileInfo resource = null;
            DirectoryInfo contentRoot;

            if ( path.StartsWith( "//" ) )
            {
                path = path.Remove( 0, 2 );
                path = path.Replace( '/', '\\' );

                contentRoot = MHEGEngine.Instance.ApplicationRoot;
                resource = new FileInfo( contentRoot.FullName +"\\"+ path );

            }
            else if ( path.StartsWith( "/" ) )
            {
                path = path.Remove( 0, 1 );
                path =  path.Replace( '/', '\\' );

                contentRoot = MHEGEngine.Instance.ActiveApplication.GroupContentFile.Directory;
                resource = new FileInfo( contentRoot.FullName +"\\"+ path );

                if ( !resource.Exists && oldContent != null && oldContent.ContentRoot != null )
                {
                    log.Warn( "Engine trying to set content: reference does not exist, trying current directory of old content ("+oldContent.ContentRoot+")." );
                    contentRoot = oldContent.ContentRoot;
                    resource = new FileInfo( contentRoot.FullName +"\\"+ path );
                }

                if ( !resource.Exists )
                {
                    log.Warn( "Find File: reference does not exist, trying application root directory("+MHEGEngine.Instance.ApplicationRoot+")." );
                    contentRoot = MHEGEngine.Instance.ApplicationRoot;
                    resource = new FileInfo( contentRoot.FullName +"\\"+ path );
                }
            }

            if ( resource.Exists && ((resource.Attributes & FileAttributes.Directory) == 0))
            {
                log.Info( "GetFile: Found resource ("+resource+")." );
                return resource;
            }
            else
            {
                log.Warn( "GetFile: Could not find resource ("+path+")." );
               // throw new NullReferenceException();
                return null;
            }
        }

        public bool CheckContent ( ContentRef reference )
        {
            String path = GetCanonicalPath( reference.Value );

            return ( GetFile( path, null ) != null );
        }

        internal bool CheckGroup ( ObjectRef reference )
        {
            String path = GetCanonicalPath( reference.GroupIdentifier );

            return ( GetFile( path, null ) != null );
        }

        private String GetCanonicalPath ( String path )
        {
            ///TODO
            if ( path.Contains( "/../" ) )
                throw new Exception( "fix me" );


            if ( path.StartsWith( "DSM:" ) )
            {
                path = path.Remove( 0, 3 );
            }
            else if ( path.StartsWith( "~" ) )
            {
                path = path.Remove( 0, 1 );
            }
            else if ( path.StartsWith( "rec:" ) || path.StartsWith( "dvb" ) )
            {
                log.Warn( "Do not support streams("+path+")." );
                return null;
            }
            else if ( path.StartsWith( "CI:" ) )
            {
                log.Warn( "Do not support conditional access("+path+")." );
                return null;
            }

            if ( !path.StartsWith( "/" ) )
            {
                log.Error( "Engine tring to set content with a  nonsense reference ("+path+")." );
                MHEGEngine.Instance.AsyncHandle( EventFactory.Create( MHEGEngine.Instance.ActiveGroup, enumEvents.EngineEvent, new GenericInteger( 3 ) ) );
                return null;
            }

            return path;
            //int nPos;
            //while ( ( nPos = csPath.find( "/../" ) ) >= 0 )
            //{
            //    ASSERT( false ); // To check.
            //    int nEnd = nPos+4;
            //    while ( nPos >= 1 && csPath [nPos-1] != '/' ) nPos--;
            //    csPath = csPath.left( nPos ) + csPath.mid( nEnd );
            //}


        }


        private Dictionary<String, ArrayList> persistantStorage = new Dictionary<string, ArrayList>();

        public Dictionary<String, ArrayList> PersistantStorage
        {
            get { return persistantStorage; }
            set { persistantStorage = value; }
        }



        public bool StoreData ( ArrayList data, String filename )
        {
            try
            {
                if ( PersistantStorage.ContainsKey( filename ) )
                    PersistantStorage.Remove( filename );
                PersistantStorage.Add( filename, data );
                log.Info("StoreData: Stored " +filename+"...");
                return true;
            }
            catch ( Exception e )
            {
                log.Error( "Engine can not store data. ("+filename+")", e );
                return false;
            }

        }

        public ArrayList GetData ( String filename )
        {
            if ( PersistantStorage.ContainsKey( filename ) )
            {
                log.Info("GetData: Retrieved " +filename+"...");
                return PersistantStorage [filename];
            }
            else
            {
                log.Error( "Engine can not read data. ("+filename+")" );
                return null;
            }

        }


        #endregion

        #region Event Code

        private Queue<ElementaryAction> mainActionQ = new Queue<ElementaryAction>();
        private Queue<ElementaryAction> tempActionQ = new Queue<ElementaryAction>();
        private Queue<Event> asyncEventQ = new Queue<Event>();
        private Dictionary<ObjectRef, Dictionary<enumEvents, List<Link>>> activeLinks = new Dictionary<ObjectRef, Dictionary<enumEvents, List<Link>>>();

        public Dictionary<ObjectRef, Dictionary<enumEvents, List<Link>>> ActiveLinks
        {
            get { return activeLinks; }
            set { activeLinks = value; }
        }


        public void ClearEventQs ()
        {
            //log.Debug( "Clearing action queues ("+mainActionQ.Count+tempActionQ.Count+asyncEventQ.Count+") items.");
            mainActionQ.Clear();
            tempActionQ.Clear();
            asyncEventQ.Clear();
        }

        public void ClearEventQs ( Group group )
        {
            //log.Debug( "Clearing action queues wrt " + group +"." );

            Queue<Event> es = new Queue<Event>();
            foreach (Event evnt in asyncEventQ )
            {
                if ( evnt.Source.ObjectIdentifier.GroupIdentifier != group.ObjectIdentifier.GroupIdentifier )
                    es.Enqueue( evnt );
            }

            asyncEventQ = es;
            mainActionQ.Clear();
            tempActionQ.Clear();
            //Queue<Action> actions = new Queue<Action>();
            //foreach (Action action in tempActionQ)
            //{
            //    if (action.

        }

        public void AddLink ( Link link )
        {
            List<Link> links;
            Dictionary<enumEvents, List<Link>> linkType;
            if ( !activeLinks.ContainsKey( link.LinkCondition.EventSource ) )
            {
                links = new List<Link>( new Link [] { link } );
                linkType = new Dictionary<enumEvents, List<Link>>();
                linkType.Add( link.LinkCondition.EventType, links );
                activeLinks.Add( link.LinkCondition.EventSource, linkType );

            }
            else
            {
                linkType = activeLinks [link.LinkCondition.EventSource];
                if ( !linkType.ContainsKey( link.LinkCondition.EventType ) )
                {
                    links = new List<Link>( new Link [] { link } );
                    linkType.Add( link.LinkCondition.EventType, links );

                }
                else
                {
                    links = linkType [link.LinkCondition.EventType];
                    links.Add( link );
                }
            }

              //log.Debug( "AddLink: "+link );
        }

        public void RemoveLink ( Link link )
        {
            List<Link> links;
            Dictionary<enumEvents, List<Link>> linkType;
            if ( activeLinks.ContainsKey( link.LinkCondition.EventSource )
                &&( linkType = activeLinks [link.LinkCondition.EventSource] )  != null )
            {
                if ( linkType.ContainsKey( link.LinkCondition.EventType )
                    && ( links = linkType [link.LinkCondition.EventType] ) != null )
                {
                    if ( links.Contains( link ) )
                    {
                        links.Remove( link );
                      //log.Debug( "RemoveLink: "+link );
                        if ( links.Count == 0 )
                        {
                           // linkType.Remove( link.LinkCondition.EventType );
                            
                            if ( linkType.Count == 0 )
                                activeLinks.Remove( link.LinkCondition.EventSource );
                        }
                    }
                }
            }
        }


        public void Handle ( Event evnt )
        {
            List<Link> links;
            Dictionary<enumEvents, List<Link>> linkType;
          //  log.Debug( "Engine processing synchronous event "+evnt+"." );

            lock ( lockObj )
            {
                if ( activeLinks.ContainsKey( evnt.Source.ObjectIdentifier ) 
                    && ( linkType = activeLinks [evnt.Source.ObjectIdentifier] ) != null  
                    &&  linkType.Count > 0 )
                {
                    if ( linkType.ContainsKey( evnt.Type ) 
                        && ( links = linkType [evnt.Type] ) != null 
                        && links.Count > 0 )
                    {
                        foreach ( Link link in links )
                        {
                            if ( link.ConditionMet( evnt ) )
                            {
                               //log.Debug( "Handle: Fireing Link"+link+"." );                    
                                foreach ( ElementaryAction ea in link.LinkEffect )
                                {
                                    tempActionQ.Enqueue( ea );
                                    //log.Debug( "Adding "+ea+" to tempActionQ." ); 
                                }
                            }
                        }
                    }

                }
            }
        }

        public void QActions ( Action actions )
        {
            foreach ( ElementaryAction ea in actions )
            {
                tempActionQ.Enqueue( ea );
               //log.Debug("QActions: Adding "+ea+".");
            }
        }

        public void DoActions ( Action actions )
        {
            if ( actions != null )
            {
                //log.Debug( "DoActions: Executing launch actions..." );
                QActions( actions );
                RunActions();
            }
        }

        public void UserInputEvent ( int key )
        {
            if ( key == 4 || key == 16 || ( key >= 100 && key <= 103 ) )
            {
                AsyncHandle( EventFactory.Create( ActiveApplication, enumEvents.EngineEvent, new GenericInteger( key ) ) );
            }

            AsyncHandle( EventFactory.Create( ActiveGroup, enumEvents.UserInput, new GenericInteger( key ) ) );

            log.Info( "User event -  pressed "+MHEGConstants.GetKeyName( key )+"." );
        }

        public void AsyncHandle ( Event evnt )
        {
            //q event on async event q
            lock ( lockObj )
            {
                asyncEventQ.Enqueue( evnt );
                //log.Debug( "Engine queing asynchronous event "+evnt.ToString()+"." );
                Monitor.PulseAll( lockObj );
            }


        }

        public void ProcessEvents ()
        {

            if ( mainActionQ.Count > 0 )
                throw new Exception( "Outstanding events in main Action Queue" );

            lock ( lockObj )
            {

                RunActions();

                //mainActionQ = tempActionQ;
                //tempActionQ = new Queue<ElementaryAction>();

                //while ( mainActionQ.Count > 0 )
                //{
                //    //mainActionQ.Dequeue().execute();
                //    ElementaryAction ea = mainActionQ.Dequeue();
                //    ea.execute();
                //    mainActionQ = ConcatQ( tempActionQ, mainActionQ );
                //    tempActionQ.Clear();
                //}

                //if(asyncEventQ.Count > 0)
                //    log.Info("Engine processing "+asyncEventQ.Count+" asynchronous events");

                while ( asyncEventQ.Count > 0 )
                {
                    //put async event's actions in tmpevntQ
                     //log.Debug("Engine processing "+asyncEventQ.Peek().ToString()+".");
                    Handle( asyncEventQ.Dequeue() );

                    if ( tempActionQ.Count >0 )
                    {


                        RunActions();


                       // mainActionQ = tempActionQ;
                        //tempActionQ = new Queue<ElementaryAction>();

                       // //  log.Info( "Engine processing "+mainActionQ.Count+" elementary actions" );

                       // while ( mainActionQ.Count > 0 )
                       // {
                       //     //  log.Info( "Engine processing " +mainActionQ.Peek().ToString()+"." );
                       //     mainActionQ.Dequeue().execute();
                       //     //   log.Info( "Resulted in "+tempActionQ.Count+" new elementary actions" );
                       //     mainActionQ = ConcatQ( tempActionQ, mainActionQ );

                       //     tempActionQ.Clear();
                       // }
                    }
                }
            }
        }


        public void RunActions ()
        {
            if ( mainActionQ.Count > 0 )
                throw new Exception( "Outstanding events in main Action Queue" );

            mainActionQ = tempActionQ;
            tempActionQ = new Queue<ElementaryAction>();

            while ( mainActionQ.Count > 0 )
            {
                //log.Debug( "Engine processing " +mainActionQ.Peek().ToString()+"." );
                mainActionQ.Dequeue().execute();
                mainActionQ = ConcatQ( tempActionQ, mainActionQ );
                tempActionQ.Clear();
            }

        }

        public Queue<T> ConcatQ<T> ( params  Queue<T> [] qs )
        {
            if ( qs [0].Count == 0 ) return qs [1];

            // Queue<T> ret = new Queue<T>( qs[0].ToArray() );



            T [] r = new T [qs [0].Count+qs [1].Count];
            qs [0].ToArray().CopyTo( r, 0 );
            qs [1].ToArray().CopyTo( r, qs [0].Count );

            Queue<T> ret = new Queue<T>( r );

            //for ( int i = 1 ; i< qs.Length ; i++ )
            //{
            //    foreach ( T t in qs[i])
            //    {
            //        ret.Enqueue( t );
            //    }
            //}

            return ret;
        }


        #endregion

        #region Timers
        private Dictionary<int, Timer> timers = new Dictionary<int, Timer>();

        public Dictionary<int, Timer> Timers
        {
            get { return timers; }
            set { timers = value; }
        }

        //timer handling
        internal void RegisterTimer ( MHEGTimer MHEGtimer )
        {
            Timer timer = new Timer( new TimerCallback( this.DoTimer ), MHEGtimer, MHEGtimer.FireTime, System.Threading.Timeout.Infinite );
            MHEGtimer.TimerHash = timer.GetHashCode();
            timers.Add( timer.GetHashCode(), timer );
            log.Info( "Adding timer" + MHEGtimer.ToString()+"." );
        }

        //internal void UpdateTimer ( MHEGTimer MHEGtimer )
        //{
        //    Timer timer = timers [MHEGtimer.Id];
        //    log.Info( "Changing timer" + timer.ToString()+" to "+ MHEGtimer.ToString() );
        //    timer.Change( MHEGtimer.FireTime, System.Threading.Timeout.Infinite );

        //}

        //internal void DeregisterTimer ( int id)
        //{
        //    Timer timer = timers [id];
        //    timers.Remove( id );
        //    timer.Dispose();
        //    log.Info( "Removing timer" + id+"." );

        //}

        public void DoTimer ( Object stateInfo )
        {
            MHEGTimer mhegTimer = ( MHEGTimer ) stateInfo;
            lock ( lockObj )
            {
                if ( mhegTimer.Group == ActiveApplication || mhegTimer.Group == ActiveGroup )
                {
                    mhegTimer.Group.FireTimer( mhegTimer );
                }

                if ( Timers.ContainsKey( mhegTimer.TimerHash ) )
                {
                    Timer timer = Timers [mhegTimer.TimerHash];
                    Timers.Remove( mhegTimer.TimerHash );
                    timer.Dispose();
                }
                Monitor.PulseAll( lockObj );
            }

        }
        #endregion

        internal void ClearTimers ()
        {
            log.Debug( "Clearing all timers..." );

            foreach ( Timer timer in timers.Values )
            {
                timer.Dispose();
            }

            timers.Clear();

        }


    }
}
